package com.jswdwsx.esign.service

import com.jswdwsx.esign.config.EsignProperties
import com.jswdwsx.esign.protocol.Token
import com.jswdwsx.esign.util.RedisUtil
import org.slf4j.LoggerFactory.getLogger
import org.springframework.stereotype.Service


@Service
class StoredTokenService(
    private val redisUtil: RedisUtil,
    private val tokenService: TokenService,
    private val esignProperties: EsignProperties
) {

    private val logger = getLogger(StoredTokenService::class.java)

    /**
     *    本地内存存一份token
     */
    val storedToken by lazy {
        mutableMapOf(
            esignProperties.identity.projectId to initToken(
                esignProperties.identity.projectId,
                esignProperties.identity.projectSecret
            ),
            esignProperties.nonIdentity.projectId to initToken(
                esignProperties.nonIdentity.projectId,
                esignProperties.nonIdentity.projectSecret
            )
        )
    }

    /**
     * 项目启动时初始化token
     */
    private fun initToken(projectId: String, projectSecret: String): Token {
        val redisTokenObj = redisUtil.hget(ESIGN_TOKEN_KEY, projectId) as Token?
        val currentTimeMillis = System.currentTimeMillis()

        // 如果redis中找到，就可以不用调用e签宝接口，防止多处部署时其他token被刷掉
        if (null != redisTokenObj) {
            logger.info("从redis中获取token成功:[$redisTokenObj]")
            if (redisTokenObj.expiresIn > currentTimeMillis && redisTokenObj.expiresIn - currentTimeMillis > 0)
                return redisTokenObj
        }
        return tokenService.getToken(projectId, projectSecret)
    }

    /**
     * 重新请求获取project的token
     */
    fun tokenForceFresh(project: EsignProperties.Project): String {
        val tokenObj = tokenService.getToken(project.projectId, project.projectSecret)
        storedToken[project.projectId] = tokenObj
        return tokenObj.token
    }

    /**
     * 获取token的方法
     */
    fun token(projectId: String, projectSecret: String): Token {
        val tokenObj = storedToken[projectId]

        // 内存中的token没过期直接使用
        if (tokenObj != null && tokenObj.expiresIn > System.currentTimeMillis()) {
            return tokenObj
        }

        // 内存中的token过期或找不到去redis取
        val redisTokenObj = redisUtil.hget(ESIGN_TOKEN_KEY, projectId).let {
            if (null != it) {
                it as Token
            } else {
                null
            }
        }
        val currentTimeMillis = System.currentTimeMillis()

        if (null != redisTokenObj) {
            // redis中取到了
            val timeLeft = redisTokenObj.expiresIn - currentTimeMillis  // 剩余有效期
            if (timeLeft > 10000) {
                // 有效期超过10s就更新内存中的token为redis中的
                storedToken[projectId] = Token(
                    token = redisTokenObj.token,
                    expiresIn = redisTokenObj.expiresIn,
                    refreshToken = redisTokenObj.refreshToken
                )
                return redisTokenObj
            } else if (redisTokenObj.expiresIn > currentTimeMillis && timeLeft < 10000 && timeLeft > 0) {
                // 有效期低于10s就异步更新redis中的token
                tokenService.asyncGetToken(projectId, projectSecret)
                return redisTokenObj
            }
        }
        // redis中没取到就调用E签宝接口获取并更新redis
        return tokenService.getToken(projectId, projectSecret)
    }

    fun catToken(): MutableMap<String, Token> {
        return storedToken
    }

    companion object {
        const val ESIGN_TOKEN_KEY = "ESIGN_TOKENS"
    }
}
